package org.wattdepot.simulation.uhcampus.building;

import java.util.Random;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.property.jaxb.Property;
import org.wattdepot.resource.sensordata.jaxb.SensorData;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Implements a simulation of photovoltaic panels generating energy on a UH campus roof.
 * Each Building's PV is initialized with an estimate of its maximum potential power in watts.
 * During nighttime hours (between 6pm and 6am), power output is zero.
 * During daylight hours, power output varies randomly between 25% and 100% of maximum power.
 * @author Philip Johnson
 */
public enum BuildingPV {
  
  /** Watanabe Hall. */
  SIM_UH_WATANABE_PV  (30000),
  /** Holmes Hall. */
  SIM_UH_HOLMES_PV    (10000),
  /** Shidler. */
  SIM_UH_BUSAD_PV      (4000),
  /** Saunders Hall. */
  SIM_UH_SAUNDERS_PV    (800);
  
  
  /** The maximum power (watts) generated by this PV. */
  private double maxPower = 0.0;
  
  /** Energy generated to date. */
  private double energyGeneratedToDate = 0;
  
  /** A random number generator. */
  private final Random random = new Random(0);

  /** The maximum random variation, initialized to 75% of the max power. */
  private int maxJiggle = 0;
  
  /** Specifies the power generated by this PV now and during the last 20 minutes. */
  private double currentPower = 0;
  
  /**
   * Initializes this building's PV based upon an estimated of its maximum power generation ability.
   * @param maxPower The estimated maximum power in watts.
   */
  private BuildingPV(double maxPower) {
    this.maxPower = maxPower;
    this.maxJiggle = (int)(maxPower * 0.75);
  }
  
  /**
   * Determine the energy generated for this client.
   * Since enums cannot extend a class, this method is duplicated in BuildingPV. Irritating.
   * @param client The WattDepotClient.
   */
  public void initializeEnergyGenerated(WattDepotClient client) {
    // First, validate the source. 
    try {
      client.getSource(name());
    }
    catch (NotAuthorizedException e) {
      System.out.format("You do not have access to the source %s. Aborting.%n", name());
      throw new RuntimeException(e);
    }
    catch (ResourceNotFoundException e) {
      System.err.format("Source %s does not exist on server. Aborting.%n", name());
      throw new RuntimeException(e);
    }
    catch (BadXmlException e) {
      System.err.println("Received bad XML from server, which is weird. Aborting.");
      throw new RuntimeException(e);
    }
    catch (MiscClientException e) {
      System.err.println("Had problems retrieving source from server, which is weird. Aborting.");
      throw new RuntimeException(e);
    }
    // Now, initialize its energy generated value. 
    try {
      this.energyGeneratedToDate = client.getLatestEnergyGeneratedToDate(name());
    }
    catch (ResourceNotFoundException e) {
      System.out.format("No prior energy generation found for source %s%n", name());
    }
    catch (Exception e) {
      System.err.format("Had problems retrieving energy to date for source %s.%n", name());
      throw new RuntimeException(e);
    }
    System.out.format("%25s energy generated to date: %12.2f kWh%n", name(), 
        (this.energyGeneratedToDate / 1000));
  }

  /**
   * Sends data about the current power generated and power generated to date to WattDepot.
   * @param client The WattDepotClient.
   */
  public void sendData(WattDepotClient client) {
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    String toolName = "UHCampusSimulation";
    String sourceURI = Source.sourceToUri(name(), client.getWattDepotUri());
    SensorData data = new SensorData(timestamp, toolName, sourceURI);
    data.addProperty(new Property(SensorData.POWER_GENERATED, currentPower));
    data.addProperty(new Property(SensorData.ENERGY_GENERATED_TO_DATE, energyGeneratedToDate));
    try {
      client.storeSensorData(data);
      System.out.format("%s %25s %10.2f kW %6.2f kWh%n", timestamp, name(), 
          (currentPower / 1000), (energyGeneratedToDate / 1000));
    }
    catch (Exception e) {
      System.out.format("Error while sending data to WattDepot: %n  %s" + e.getMessage());
    }
  }
  
  /**
   * Returns true if this interval is between 6pm and 6am.
   * @param interval The interval of the day.
   * @return True if it's during the night. 
   */
  private boolean isNight(int interval) {
    return ((interval < 18) || (interval > 54));
  }

  /**
   * Updates the currentPower and the powerGeneratedToDate based upon the current interval.
   * @param interval The interval.
   */
  public void update(int interval) {
    if (isNight(interval)) {
      this.currentPower = 0;
    }
    else {
      int jiggle = random.nextInt(this.maxJiggle);
      this.currentPower = this.maxPower - jiggle;
      this.energyGeneratedToDate += (this.currentPower / 3);
    }
  }
}
